#ifndef _I2C_HW_H_
#define _I2C_HW_H_

#include "soc.h"

/**
 * 1、I2C兼容SMBus 2.0(系统管理总线)和PMBus(电源管理总线)
 * 2、支持并行总线/I2C协议转换
 * 3、支持多主模式，默认为从模式，生成起始位后进入主模式，生成停止位后自动切回从模式
 * 4、支持标准速度100KHz和快速速度400KHz
 * 5、支持CRC和PEC(数据包错误校验)生成或验证
 */

typedef struct {
	_RW CR1;        /*!< I2C Control register 1,     offset: 0x00 */
	_RW CR2;        /*!< I2C Control register 2,     offset: 0x04 */
	_RW OAR1;       /*!< I2C Own address register 1, offset: 0x08 */
	_RW OAR2;       /*!< I2C Own address register 2, offset: 0x0C */
	_RW DR;         /*!< I2C Data register,          offset: 0x10 */
	_RW SR1;        /*!< I2C Status register 1,      offset: 0x14 */
	_OR SR2;        /*!< I2C Status register 2,      offset: 0x18 */
	_RW CCR;        /*!< I2C Clock control register, offset: 0x1C */
	_RW TRISE;      /*!< I2C TRISE register,         offset: 0x20 */
} I2C_Struct;

extern I2C_Struct SOC_I2C1;
extern I2C_Struct SOC_I2C2;
extern I2C_Struct SOC_I2C3;


/**
 * 控制，STOP、 START 或 PEC 位置 1 之后，在硬件将该位清零之前，软件不能对 I2C_CR1 执行任何写操作。否则，可能会再次发出 STOP、 START 或 PEC 置位请求。
 */
#define I2C_CR1_PE_DIS                 (0x0U<<0)
#define I2C_CR1_PE_EN                  (0x1U<<0)  // 使能外设，在主模式下，此位不能在通信结束之前复位
#define I2C_CR1_MODE_I2C               (0x0U<<1)  // I2C模式
#define I2C_CR1_MODE_SMBUS             (0x1U<<1)  // SMBus模式
#define I2C_CR1_SMBUS_SLAVE            (0x0U<<3)  // SMBus设备端
#define I2C_CR1_SMBUS_MASTER           (0x1U<<3)
#define I2C_CR1_ENARP_DIS              (0x0U<<4)
#define I2C_CR1_ENARP_EN               (0x1U<<4)  // 使能ARP
#define I2C_CR1_ENPEC_DIS              (0x0U<<5)
#define I2C_CR1_ENPEC_EN               (0x1U<<5)  // 使能PEC计算(数据包错误校验)
#define I2C_CR1_ENGC_DIS               (0x0U<<6)
#define I2C_CR1_ENGC_EN                (0x1U<<6)  // 使能广播呼叫，对地址00h应答
#define I2C_CR1_STRETCH_DIS            (0x1U<<7)  // 禁止时钟延长，在从模式下当ADDR或BTF标志置一时，此位用于禁止时钟延长，直到软件将其复位为止。
#define I2C_CR1_STRETCH_EN             (0x0U<<7)
#define I2C_CR1_START_DIS              (0x0U<<8)
#define I2C_CR1_START_EN               (0x1U<<8)  // 此位由软件置 1 和清零，并可在起始位发送完成后或 PE=0 时由硬件清零。在主模式下，写1硬件会生成重复起始位，在从模式下，写1硬件会在总线空闲时生成起始位。
#define I2C_CR1_STOP_DIS               (0x0U<<9)
#define I2C_CR1_STOP_EN                (0x1U<<9)  // 该位由软件置 1 和清零，也可在检测到停止位时由硬件清零，在检测到超时错误时由硬件置1。在主模式下，写1硬件会在传输当前字节或发送当前起始位后生成停止位。在从模式下，写1硬件会完成当前字节传输后释放 SCL 和 SDA 线
#define I2C_CR1_ACK_DIS                (0x0U<<10)
#define I2C_CR1_ACK_EN                 (0x1U<<10) // 此位由软件置 1 和清零，并可在 PE=0 时由硬件清零。写1硬件会在接收一个字节（匹配地址或数据）之后返回应答
#define I2C_CR1_POS_CUR                (0x0U<<11) // ACK位控制的是当前字节
#define I2C_CR1_POS_NEXT               (0x1U<<11) // ACK位控制的的是下一个字节
#define I2C_CR1_PEC_DIS                (0x0U<<12)
#define I2C_CR1_PEC_EN                 (0x1U<<12) // 使能PEC传输
#define I2C_CR1_ALERT_NACK             (0x0U<<13) // 释放 SMBA 引脚使其变成高电平。报警响应地址头后跟 NACK
#define I2C_CR1_ALERT_ACK              (0x1U<<13) // 驱动 SMBA 引脚使其变成低电平。报警响应地址头后跟 ACK
#define I2C_CR1_SWRST_DIS              (0x0U<<15)
#define I2C_CR1_SWRST_EN               (0x1U<<15) // I2C软复位

/**
 * 控制2
 */
#define I2C_CR2_FREQ_V_2_42D(val)      ((val)<<0) // 外设时钟频率，100KHz速率最小时钟为2MHz，400KHz最小时钟为4MHz，APB最大时钟为42MHz，该值应等于APB的时钟频率
#define I2C_CR2_ITERREN_DIS            (0x0U<<8)
#define I2C_CR2_ITERREN_EN             (0x1U<<8)  // 使能错误中断,BERR/ARLO/AF/OVR/PECERR/TIMEOUT/SMBALERT
#define I2C_CR2_ITEVTEN_DIS            (0x0U<<9)
#define I2C_CR2_ITEVTEN_EN             (0x1U<<9)  // 使能事件中断，SB(主)/ADDR(主从)/ADDR10(主)/STOPF(从)/BTF(无TxE或RxNE事件)/ITBUFEN(且TxE事件置1)/ITBUFEN(且RxNE事件置1)
#define I2C_CR2_ITBUFEN_DIS            (0x0U<<10)
#define I2C_CR2_ITBUFEN_EN             (0x1U<<10) // TxE = 1 或 RxNE = 1 时生成事件中断（与 DMAEN 状态无关）
#define I2C_CR2_DMAEN_DIS              (0x0U<<11)
#define I2C_CR2_DMAEN_EN               (0x1U<<11) // 当 TxE=1 或 RxNE =1 时使能 DMA 请求
#define I2C_CR2_LAST_DIS               (0x0U<<12)
#define I2C_CR2_LAST_EN                (0x1U<<12) // 下一个 DMA EOT 是最后一次传输，此位用于主接收模式，可对最后接收的数据生成 NACK。

/**
 * 自有地址1
 */
#define I2C_OAR1_ADDR10(val)           ((val)<<0)
#define I2C_OAR1_ADDR7(val)            ((val)<<1)
#define I2C_OAR1_MODE_7B               (0x1U<<14) // 7位从地址（无法应答10位地址）
#define I2C_OAR1_MODE_10B              (0x3U<<14) // 10位从地址（无法应答7位地址）

/**
 * 自有地址2
 */
#define I2C_OAR2_ENDUAL_DIS            (0x0U<<0)  // 7 位寻址模式下仅对 OAR1 地址响应
#define I2C_OAR2_ENDUAL_EN             (0x1U<<0)  // 7 位寻址模式下能对 OAR1 和 OAR2 两个地址响应
#define I2C_OAR2_ADDR7(val)            ((val)<<1)

/**
 * 数据寄存器，在 DR 寄存器中写入第一个字节时自动开始发送字节，在从模式下，地址并不会复制到 DR 中，硬件不对写冲突进行管理（ TxE=0 时也可对 DR 执行写操作）
 */
#define I2C_DR_

/**
 * 状态1
 */
#define I2C_SR1_SB_EN                  (0x1U<<0)  // 生成启动条件时置1，由软件在读取 SR1 寄存器后写入 DR 寄存器来清零
#define I2C_SR1_ADDR_EN                (0x1U<<1)  // 从模式表示接收到的地址匹配，主模式表示地址发送结束（收到ACK），由软件在读取 SR1 寄存器后读取 SR2 寄存器来清零
#define I2C_SR1_BTF_EN                 (0x1U<<2)  // 数据字节传输成功完成，需要满足STRETCH_EN(使能时钟延长)，同时接收过程中接收到一个新字节（包括 ACK 脉冲）但尚未读取 DR 寄存器 (RxNE=1)或发送过程中将发送一个新字节但尚未向 DR 寄存器写入数据 (TxE=1)。由软件读或写 DR 寄存器来清零，或在发送过程中出现起始或停止位后由硬件清零
#define I2C_SR1_ADDR10_EN              (0x1U<<3)  // 主器件已发送第一个地址字节（头），由软件在读取 SR1 寄存器后在 DR 寄存器中写入第二个地址字节来清零，收到 NACK 后 ADD10 位不会置1
#define I2C_SR1_STOPF_EN               (0x1U<<4)  // 从设备在应答脉冲后（如果 ACK=1）检测到停止位，由硬件置 1，收到 NACK 后 STOPF 位不会置 1。由软件分别对 SR1 寄存器和 CR1 寄存器执行读操作和写操作来清零
#define I2C_SR1_RxNE_EN                (0x1U<<6)  // 接收模式下数据寄存器非空时置 1。 RxNE 不会在地址阶段置 1。由软件读取或写入 DR 寄存器来清零，或在 PE=0 时由硬件清零。BTF 置 1 时无法通过读取数据将 RxNE 清零，因为此时数据寄存器仍为满。
#define I2C_SR1_TxE_EN                 (0x1U<<7)  // 发送过程中 DR 为空时该位置 1。 TxE 不会在地址阶段置 1，由软件写入 DR 寄存器来清零，或在出现起始、停止位或者 PE=0 时由硬件清零。如果接收到 NACK 或要发送的下一个字节为 PEC (PEC=1)， TxE 将不会置 1
#define I2C_SR1_BERR_EN                (0x1U<<8)  // 存在误放的起始或停止位，由软件写入 0 来清零，或在 PE=0 时由硬件清零
#define I2C_SR1_ARLO_EN                (0x1U<<9)  // 当接口在竞争总线是输给另一个主设备时，由硬件将该位置1，由软件写入 0 来清零，或在 PE=0 时由硬件清零
#define I2C_SR1_AF_EN                  (0x1U<<10) // 应答失败，由软件写入 0 来清零，或在 PE=0 时由硬件清零
#define I2C_SR1_OVR_EN                 (0x1U<<11) // 从模式下满足STRETCH_DIS(禁止时钟延长)，出现DR上下溢出，由软件写入 0 来清零，或在 PE=0 时由硬件清零
#define I2C_SR1_PECERR_EN              (0x1U<<12) // PEC 错误：接收器在接收 PEC 后返回 NACK（无论 ACK 什么值），由软件写入 0 来清零，或在 PE=0 时由硬件清零
#define I2C_SR1_TIMEOUT_EN             (0x1U<<14) // 主器件累计时钟低电平延长时间超过 10 ms，从器件累计时钟低电平延长时间超过 25 ms，在从模式下置 1 时：从器件复位通信且硬件释放数据线，在主模式下置 1 时：由硬件发送停止位，由软件写入 0 来清零，或在 PE=0 时由硬件清零，此功能仅在 SMBus 模式下可用
#define I2C_SR1_SMBALERT_EN            (0x1U<<15) // 在 SMBus 主机模式下：引脚上发生 SMBALERT 事件，在 SMBus 从模式下：接收到指示 SMBALERT 低电平的 SMBALERT 响应地址头，由软件写入 0 来清零，或在 PE=0 时由硬件清零

/**
 * 状态2
 */
#define I2C_SR2_MSL_EN                 (0x1U<<0)  // 接口进入主模式时 (SB=1) 由硬件置 1。检测到总线上的停止位、仲裁丢失 (ARLO=1) 或当 PE=0 时由硬件清零
#define I2C_SR2_BUSY_EN                (0x1U<<1)  // 检测到 SDA 或 SCL 低电平时由硬件置1，检测到停止位时由硬件清零。该位指示总线上是否正在进行通信。即使禁止接口 (PE=0) 后此信息也会更新
#define I2C_SR2_TRA_EN                 (0x1U<<1)  // 此位在整个地址阶段的结尾处根据地址字节的 R/W 位状态进行置1，1表示发送，检测到停止位 (STOPF=1)、重复起始位、总线仲裁丢失 (ARLO=1) 或当 PE=0 时该位也由硬件清零。
#define I2C_SR2_GENCALL_EN             (0x1U<<1)  // ENGC=1 时接收到广播呼叫地址，出现停止位、重复起始位或 PE=0 时由硬件清零
#define I2C_SR2_SMBDEFAULT_EN          (0x1U<<1)  // ENARP=1 时接收到 SMBus 器件默认地址，出现停止位、重复起始位或 PE=0 时由硬件清零
#define I2C_SR2_SMBHOST_EN             (0x1U<<1)  // SMBTYPE=1 且 ENARP=1 时接收到 SMBus 主机地址，出现停止位、重复起始位或 PE=0 时由硬件清零
#define I2C_SR2_DUALF_EN               (0x1U<<1)  // 0：接收到的地址与 OAR1 匹配，1：接收到的地址与 OAR2 匹配，出现停止位、重复起始位或 PE=0 时由硬件清零
#define I2C_SR2_PEC(dev)               ((dev).SR2>>8)  // ENPEC=1 时，此寄存器包含内部 PEC

/**
 * 时钟控制，APB的时钟最大为42MHz，Tapb=1/42M=2.4ns
 * I2C主模式/SMBus，占空比为50%:
 *    时钟高电平: Th=CCR*Tapb
 *    时钟低电平: Tl=CCR*Tapb
 * 快速模式：
 *    占空比33%：
 *        Th=CCR*Tapb*1
 *        Tl=CCR*Tapb*2
 *    占空比36%：
 *        Th=CCR*Tapb*9
 *        Tl=CCR*Tapb*16
 */
#define I2C_CCR_V_1_4095D(val)         ((val)<<0)
#define I2C_CCR_DUTY_33                (0x0U<<14)  // Th*1 Tl*2
#define I2C_CCR_DUTY_36                (0x1U<<14)  // Th*9 Tl*16
#define I2C_CCR_FS_STD                 (0x0U<<15)  // 标准模式最大100KHz
#define I2C_CCR_FS_FAST                (0x1U<<15)  // 快速模式最大400KHz

/**
 * 快速/标准模式下的最大上升时间，默认值为2，代表(2-1)*Tapb=2.4ns
 */
#define I2C_TRISE_V_0_62D(val)         (((val)+1)<<0)

/**
 * 滤波器配置，F42x才会有
 */
#define I2C_FLTR_

#endif /* _I2C_HW_H_ */
